﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text.RegularExpressions;
using SS.Common.DBBuilder.DBDataStructure;
using SS.Common.DBBuilder.DBInteraction;
using SS.Common.Logging;

namespace SS.Common.DBBuilder
{
    /// <summary>
    /// The class implement interface IDataContext that is used to create DB structure
    /// </summary>
    public abstract class AbstractDataContext: IDataContext
    {
        #region Fields

        /// <summary>
        /// The instance of IDataProvider
        /// </summary>
        protected IDataProvider _provider;

        /// <summary>
        /// The logger instance
        /// </summary>
        protected static readonly ILogger Logger = LoggerManager.GetLogger(typeof(AbstractDataContext).Name);

        /// <summary>
        /// Defines list of tables are restricted to build
        /// </summary>
        private readonly Regex[] _restrictionTables = { 
                                                          new Regex("EdmMetadata", RegexOptions.IgnoreCase), 
                                                          new Regex("sysdiagrams", RegexOptions.IgnoreCase),
                                                          new Regex("__MigrationHistory", RegexOptions.IgnoreCase),
                                                          new Regex("__ExtendedProperties", RegexOptions.IgnoreCase),
                                                          new Regex("aspnet.*", RegexOptions.IgnoreCase) 
                                                      };

        #endregion

        #region Methods

        /// <summary>
        /// Inititalize database structure
        /// </summary>
        /// <returns></returns>
        public DataBase InitDBStructure()
        {
            Logger.Info("InitDBStructure --> Start");

            DataBase db = null;
            // Step1: init db structure
            var tbls = this.GetDataTables();
            if (tbls.Any())
            {
                db = new DataBase(tbls);
                // Step2: update FKs and PKs
                this.SetKeys(db);
            }

            Logger.Info("InitDBStructure <-- End");

            return db;
        }

        /// <summary>
        /// Get list of tables
        /// </summary>
        /// <returns></returns>
        protected IEnumerable<DataTable> GetDataTables()
        {
            Logger.Info("GetDataTables --> Start");

            var lstDataTbl = new List<DataTable>();
            var lstTable = this.GetTables();

            // Determines whether table is restricted
            Func<string, bool> isNotRestricted = inputTable => 
                _restrictionTables.All(restrictionTable => !restrictionTable.IsMatch(inputTable));

            foreach (string table in lstTable)
            {
                if (isNotRestricted(table))
                {
                    var ds = new DataSet();                    
                    _provider.Query = string.Format("Select * From [{0}] ", table);
                    _provider.FillDataSet(ref ds, table);

                    if (ds.Tables[table] != null)
                    {
                        lstDataTbl.Add(ds.Tables[table]);
                    }
                }
            }

            Logger.Info("GetDataTables <-- End");

            return lstDataTbl;
        }

        /// <summary>
        /// Get list of tables of database
        /// </summary>
        /// <returns></returns>
        protected IEnumerable<string> GetTables()
        {
            Logger.Info("GetTables --> Start");

            var ds = new DataSet();
            _provider.Query = @"select table_name from information_schema.tables where TABLE_TYPE <> 'VIEW'";
            _provider.FillDataSet(ref ds, "ListTable");

            var tbls = ds.Tables["ListTable"].Rows.Cast<DataRow>().Select(n => n[0].ToString());

            Logger.Info("GetTables <-- End");

            return tbls;
        }

        /// <summary>
        /// Update foreign keys and primary keys for all tables of database
        /// </summary>
        /// <param name="DB"></param>
        protected abstract void SetKeys(DataBase DB);

        #endregion
    }
}

